//
// Created by whz on 2022/5/22.
//

#ifndef B_PLUS_DB_BUFFER_POOL_H
#define B_PLUS_DB_BUFFER_POOL_H

#include <unordered_map>
#include <unordered_set>
#include <list>
#include <fstream>
#include <iostream>
#include <thread>
#include <memory>
#include <algorithm>
#include "../innodb/page.h"
#include "../common/config.h"
#include "flush_queue.h"

struct checkpoint {
    //全局页号计数
    size_t count;
    //延迟删除数组游标
    int cursor;
    //延迟删除数组
    size_t free_array[100];
    //根结点页号
    size_t root_no;
};


class BufferPool {
public:
    /**
     * 申请页面 by page_no
     *
     * page_no: 页号 read_only: 是否只读，决定页面是否刷盘
     *
     * 默认page_no=-1,返回新页(页号自动递增)
     *
     * @return  PAGE *
     */
    PAGE *allocate(size_t page_no = -1, bool read_only = false);

    /**
     * 获取root页
     * @return  PAGE *
     */
    PAGE *get_root();

    /**
     * 设置根节点
     * @param page
     */
    void set_root(PAGE *page);

    /**
     * 释放空闲页面
     * 延迟删除->加入空闲列表
     */
    void release(PAGE *page);

    /**
     * 上锁 防止页面置换
     * @param page
     */
    void lock(PAGE *page);

    /**
       * 解锁
       * @param page
       */
    void unlock(PAGE *page);

    /**
     * 在销毁时必须调用！！！！
     * 最后刷盘确认
     */
    void destroy();

    static std::shared_ptr<BufferPool> getInstance() {
        if (instance == nullptr) {
            instance.reset(new BufferPool);
        }
        return instance;
    }

    ~BufferPool();


private:

    BufferPool();

    BufferPool(const BufferPool &);

    BufferPool &operator=(const BufferPool &);

    static bool check_file(const std::string &path = PATH_COMMIT);

    /* read block from disk */
    void serialized(PAGE *block, size_t page_no);

    /* write block to disk */
    static void deserialized(PAGE *block, size_t page_no);

    void load_checkpoint() const;

    void dump_checkpoint() const;

    void open_mmap();

    void close_mmap();

    void read_mmap(PAGE *block, off_t offset);

    void read_stream(PAGE *block, off_t offset);

    void f_read(PAGE *block, off_t offset);

    static void f_write(PAGE *block, off_t offset);

    static void write_stream(PAGE *block, off_t offset);

    /*   初始化   */
    void init_empty();

    void check_fill();

    /* 获取页号 */
    size_t acquire();

    /*   刷盘以及释放内存   */
    void flush(PAGE *page);


    class FlushService {
    private:
        bool is_run = false;
        std::unique_ptr<std::thread> flush_thread;
    public:
        explicit FlushService() = default;

        void start() {
            is_run = true;
//            std::cout << "FlushService start" << std::endl;
            flush_thread = std::unique_ptr<std::thread>(new std::thread(&FlushService::run, this));
//            flush_thread = new std::thread(&FlushService::run, this);
        }

        void stop() {
//            std::cout << "FlushService stop" << std::endl;
            is_run = false;
            flush_queue.push(nullptr);
            if (flush_thread->joinable()) {
                flush_thread->join();
            }
        }

        void run() const {
            while (is_run || !flush_queue.empty()) {
                //睡眠整流，避免大量刷盘带来性能抖动
//                std::this_thread::sleep_for(std::chrono::milliseconds(1));
//                std::cout << "FlushService flush all" << flush_queue.size() << std::endl;
                auto flush_page = flush_queue.wait_and_pop();
                if (flush_page != nullptr) {
                    deserialized(flush_page, flush_page->getPageNo());
                }
                //无论怎样指针都是要释放的
                delete flush_page;
                flush_page = nullptr;
            }
        }

    };

private:
    static std::shared_ptr<BufferPool> instance;

    std::unordered_map<size_t, PAGE *> page_table;
    //probably don't need a flush set
//TODO 可能select时候可以优化此部分，select无需刷盘
    std::unordered_set<PAGE *> flush_set;

    static FlushQueue<PAGE *> flush_queue;

    std::list<PAGE *> page_list;

    //没有去重依赖手工维护
    std::list<size_t> free_list;

    std::unordered_set<PAGE *> lock_set;

    checkpoint conf{};

//    PAGE *root;

    std::shared_ptr<FlushService> flush_service;

    char *buf;

};


#endif //B_PLUS_DB_BUFFER_POOL_H
